/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * ArkUI头文件中没有对定义的OH_PixelmapNative使用typedef
 * 纯C使用会导致编译时arkui头文件函数入参报错，这里在include前定义一下
 */
typedef struct OH_PixelmapNative OH_PixelmapNative;

#include <stdbool.h>
#include <stdlib.h>
#include <arkui/native_node.h>
#include <arkui/native_interface.h>
#include <hilog/log.h>
#include "imageknifec_test.h"
#include "imageknifec.h"

ArkUI_NativeNodeAPI_1 *GetNodeAPI()
{
    static ArkUI_NativeNodeAPI_1 *g_nativeNodeAPI = NULL;
    if (g_nativeNodeAPI == NULL) {
        OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, g_nativeNodeAPI);
    }
    return g_nativeNodeAPI;
}

NativeImage *CreateComponent(bool isAnimator)
{
    NativeImage *nativeImage = (NativeImage *)malloc(sizeof(NativeImage));
    if (nativeImage == NULL) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "IMAGE_KNIFE_PRO", "allocate memory failed");
        return NULL;
    }
    nativeImage->isLoading = true;
    nativeImage->markDestroy = false;
    nativeImage->isAnimator = isAnimator;

    if (isAnimator) {
        nativeImage->handle = GetNodeAPI()->createNode(ARKUI_NODE_IMAGE_ANIMATOR);
        // 设置动图组件默认循环播放
        ArkUI_NumberValue value = {.i32 = ARKUI_ANIMATION_STATUS_RUNNING};
        ArkUI_AttributeItem item = {&value, 1};
        GetNodeAPI()->setAttribute(nativeImage->handle, NODE_IMAGE_ANIMATOR_STATE, &item);
        value.i32 = -1;
        GetNodeAPI()->setAttribute(nativeImage->handle, NODE_IMAGE_ANIMATOR_ITERATION, &item);
    } else {
        nativeImage->handle = GetNodeAPI()->createNode(ARKUI_NODE_IMAGE);
    }

    // 设置组件宽高
    const float width = 200;
    ArkUI_NumberValue value = {.f32 = width};
    ArkUI_AttributeItem item = {&value, 1};
    GetNodeAPI()->setAttribute(nativeImage->handle, NODE_WIDTH, &item);
    GetNodeAPI()->setAttribute(nativeImage->handle, NODE_HEIGHT, &item);
    return nativeImage;
}

void DisposeNativeImage(NativeImage *nativeImage)
{
    if (nativeImage->handle != NULL) {
        if (nativeImage->isAnimator) {
            GetNodeAPI()->resetAttribute(nativeImage->handle, NODE_IMAGE_ANIMATOR_IMAGES);
        }
        GetNodeAPI()->disposeNode(nativeImage->handle);
    }
    free(nativeImage);
}

ArkUI_NodeHandle GetHandleFromUserContext(void *usrCtx)
{
    if (usrCtx == NULL) {
        return NULL;
    }

    NativeImage *nativeImage = (NativeImage *)usrCtx;
    nativeImage->isLoading = 0;
    // 组件已下树，需要销毁
    if (nativeImage->markDestroy) {
        DisposeNativeImage(nativeImage);
        return NULL;
    }

    return nativeImage->handle;
}

void ReleaseNativeImage(NativeImage *nativeImage)
{
    if (nativeImage == NULL) {
        return;
    }

    if (nativeImage->isLoading) {
        // 图片还在加载中，但组件已经下树，进行标记交由callback进行析构
        nativeImage->markDestroy = true;
        return;
    }

    DisposeNativeImage(nativeImage);
}

/**
 * 示例一：ImageknifecGetCacheImageByLoadSrc获取单帧静态图，并使用Image组件显示
 * 由于回调在主线程中触发，因此整个示例过程无需考虑多线程竞争问题
 * ArkUI约束：ArkUI_NativeNodeAPI_1获取的API需要在主线程中调用
 */
void GetCacheImageCallback(void *usrCtx, ImageknifecImageData *imageData)
{
    ArkUI_NodeHandle handle = GetHandleFromUserContext(usrCtx);
    if (handle == NULL) {
        ImageknifecImageDataRelease(imageData);
        return;
    }

    // 获取的图片第0帧pixelmap
    OH_PixelmapNative *pixelmap = NULL;
    ImageknifecError errorCode = ImageknifecImageDataGetPixelmap(imageData, &pixelmap);
    if (errorCode) {
        ImageknifecImageDataRelease(imageData);
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "IMAGE_KNIFE_PRO",
                     "ImageknifecImageDataGetPixelmap failed:%{public}d", errorCode);
        return;
    }

    // 将图片pixelmap设置给Image组件进行渲染
    ArkUI_DrawableDescriptor *drawableDesc =  OH_ArkUI_DrawableDescriptor_CreateFromPixelMap(pixelmap);
    ArkUI_AttributeItem item = {.object = drawableDesc};
    GetNodeAPI()->setAttribute(handle, NODE_IMAGE_SRC, &item);
    OH_ArkUI_DrawableDescriptor_Dispose(drawableDesc);

    // ImageData不使用后进行释放
    ImageknifecImageDataRelease(imageData);
}

NativeImage *CreateNativeImageWithUrl(const char *url)
{
    // 创建一个Image组件，用于示例展示获取的图片
    NativeImage *nativeImage = CreateComponent(false);
    if (nativeImage == NULL) {
        return NULL;
    }

    // 设置GetCacheImage纯C接口的回调
    ImageknifecGetCacheImageCallbackObject callbackObject;
    callbackObject.callback = GetCacheImageCallback;
    callbackObject.usrCtx = nativeImage;
    // 尝试从缓存获取url对应的图片内容，没有缓存则去加载图片
    ImageknifecError errorCode = ImageknifecGetCacheImageByLoadSrc(url, &callbackObject,
                                                                   IMAGE_KNIFE_CACHE_STRATEGY_DEFAULT, NULL);
    if (errorCode) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "IMAGE_KNIFE_PRO",
                     "ImageknifecGetCacheImageByLoadSrc failed:%{public}d", errorCode);
        nativeImage->isLoading = 0;
        ReleaseNativeImage(nativeImage);
        nativeImage = NULL;
    }

    // 返回Image handel由上层业务进行组件上树，布局
    return nativeImage;
}

void SetAnimatorImages(ArkUI_NodeHandle handle, OH_PixelmapNative **pixelmapList,
                       int *delayTimeList, uint32_t frameCount)
{
    if (handle == NULL || pixelmapList == NULL || delayTimeList == NULL || frameCount < 1) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "IMAGE_KNIFE_PRO", "SetAnimatorImages failed, Invalid parameter");
        return;
    }

    // 封装Animator显示动图所需的frameInfo，并设置给组件显示
    ArkUI_DrawableDescriptor **drawableDescList =
        (ArkUI_DrawableDescriptor **)malloc(sizeof(ArkUI_DrawableDescriptor *) * frameCount);
    if (drawableDescList == NULL) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "IMAGE_KNIFE_PRO", "Allocate memory failed");
        return;
    }
    ArkUI_ImageAnimatorFrameInfo **frameInfoList =
        (ArkUI_ImageAnimatorFrameInfo **)malloc(sizeof(ArkUI_ImageAnimatorFrameInfo *) * frameCount);
    if (frameInfoList == NULL) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "IMAGE_KNIFE_PRO", "Allocate memory failed");
        free(drawableDescList);
        return;
    }

    for (int i = 0; i < frameCount; i++) {
        drawableDescList[i] = OH_ArkUI_DrawableDescriptor_CreateFromPixelMap(pixelmapList[i]);
        frameInfoList[i] = OH_ArkUI_ImageAnimatorFrameInfo_CreateFromDrawableDescriptor(drawableDescList[i]);
        OH_ArkUI_ImageAnimatorFrameInfo_SetDuration(frameInfoList[i], delayTimeList[i]);
    }
    ArkUI_AttributeItem item = {.size = frameCount, .object = frameInfoList};
    GetNodeAPI()->setAttribute(handle, NODE_IMAGE_ANIMATOR_IMAGES, &item);

    // 释放资源
    for (int i = 0; i < frameCount; i++) {
        OH_ArkUI_DrawableDescriptor_Dispose(drawableDescList[i]);
        OH_ArkUI_ImageAnimatorFrameInfo_Dispose(frameInfoList[i]);
    }
    free(drawableDescList);
    free(frameInfoList);
}

/**
 * 示例二：ImageknifecGetCacheImageByLoadSrc获取多帧动图图，并使用Animator组件循环播放动图
 */
void GetCacheImageCallbackMultiFrame(void *usrCtx, ImageknifecImageData *imageData)
{
    ArkUI_NodeHandle handle = GetHandleFromUserContext(usrCtx);
    if (handle == NULL) {
        ImageknifecImageDataRelease(imageData);
        return;
    }

    // 获取图片帧数
    uint32_t frameCount = 0;
    ImageknifecError errorCode = ImageknifecImageDataGetFrameCount(imageData, &frameCount);
    if (errorCode || frameCount < 1) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "IMAGE_KNIFE_PRO",
                     "ImageknifecImageDataGetFrameCount failed:%{public}d", errorCode);
        ImageknifecImageDataRelease(imageData);
        return;
    }

    // 获取图片所有pixelmap
    OH_PixelmapNative **pixelmapList = (OH_PixelmapNative **)malloc(sizeof(OH_PixelmapNative *) * frameCount);
    if (pixelmapList == NULL) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "IMAGE_KNIFE_PRO", "Allocate memory failed");
        ImageknifecImageDataRelease(imageData);
        return;
    }
    errorCode = ImageknifecImageDataGetPixelmapList(imageData, pixelmapList, frameCount);
    if (errorCode) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "IMAGE_KNIFE_PRO",
                     "ImageknifecImageDataGetPixelmapList failed:%{public}d", errorCode);
        ImageknifecImageDataRelease(imageData);
        free(pixelmapList);
    }

    // 获取每帧播放时长
    int *delayTimeList = (int *)malloc(sizeof(int) * frameCount);
    if (delayTimeList == NULL) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "IMAGE_KNIFE_PRO", "Allocate memory failed");
        ImageknifecImageDataRelease(imageData);
        free(pixelmapList);
        return;
    }
    errorCode = ImageknifecImageDataGetDelayTimeList(imageData, delayTimeList, frameCount);
    if (errorCode) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "IMAGE_KNIFE_PRO",
                     "ImageknifecImageDataGetDelayTimeList failed:%{public}d", errorCode);
        // 帧播放时长缺失，设置每帧播放时间为100ms
        const int defaultPlayTime = 100;
        for (int i = 0; i < frameCount; i++) {
            delayTimeList[i] = defaultPlayTime;
        }
    }

    // 设置给动图组件显示
    SetAnimatorImages(handle, pixelmapList, delayTimeList, frameCount);

    // 不使用后释放
    ImageknifecImageDataRelease(imageData);
    free(pixelmapList);
    free(delayTimeList);
}

NativeImage *CreateNativeAnimatorWithUrl(const char *url)
{
    // 创建动图组件，用于展示获取的图片
    NativeImage *nativeImage = CreateComponent(true);
    if (nativeImage == NULL) {
        return NULL;
    }

    // 设置GetCacheImage纯C接口的回调
    ImageknifecGetCacheImageCallbackObject callbackObject;
    callbackObject.callback = GetCacheImageCallbackMultiFrame;
    callbackObject.usrCtx = nativeImage;
    // 尝试从缓存获取url对应的图片内容，没有缓存则去加载图片
    ImageknifecError errorCode = ImageknifecGetCacheImageByLoadSrc(url, &callbackObject,
                                                                   IMAGE_KNIFE_CACHE_STRATEGY_DEFAULT, NULL);
    if (errorCode) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "IMAGE_KNIFE_PRO",
                     "ImageknifecGetCacheImageByLoadSrc failed:%{public}d", errorCode);
        nativeImage->isLoading = 0;
        ReleaseNativeImage(nativeImage);
        nativeImage = NULL;
    }

    // 返回Animator handel由上层业务进行组件上树，布局
    return nativeImage;
}
